Modern CSS Guide 2026
Production-Ready Reference for Senior Engineersβ
Table of Contentsβ
- Layout Fundamentals
- Responsive Design
- Visual Effects
- Interaction & UX
- Performance Optimization
- Modern Selectors
- Animations
- Container Queries
- Production Starter Template
- Interview Tips
Layout Fundamentalsβ
Flexboxβ
Why it matters: Still the most practical solution for one-dimensional layouts. Non-negotiable for any modern frontend role.
.container {
display: flex;
gap: 16px; /* Modern: replaces margin hacks */
align-items: center;
justify-content: space-between;
}
.item {
flex: 1 1 auto; /* Shorthand: grow shrink basis */
align-self: flex-start; /* Override parent alignment */
}
Key modern additions:
gapproperty (now works in flexbox, not just grid)align-selffor individual item control
Real-world use cases:
- Navigation bars
- Toolbars
- Button groups
- Single-row/column layouts
Gridβ
Why it matters: Mandatory for senior roles. The most powerful layout system for two-dimensional designs.
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
}
Critical patterns interviewers expect:
auto-fit vs auto-fillβ
/* auto-fit: collapses empty tracks */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
/* auto-fill: preserves empty tracks */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
When to use:
auto-fitβ Stretch items to fill spaceauto-fillβ Maintain grid structure even with few items
minmax()β
/* Item is at least 240px, can grow to fill available space */
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
fr unitsβ
/* Fractional units: distributes available space */
grid-template-columns: 1fr 2fr 1fr; /* middle column is 2x wider */
Named grid areasβ
.layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar content content"
"footer footer footer";
grid-template-columns: 200px 1fr 1fr;
grid-template-rows: auto 1fr auto;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }
Real-world use cases:
- Dashboard layouts
- Card grids
- Magazine-style layouts
- Complex page structures
Responsive Designβ
Fluid Typography with clamp()β
Why it matters: Eliminates media query juggling for font sizes. Essential for modern responsive design.
h1 {
font-size: clamp(1.5rem, 2.5vw, 3rem);
/* min: 1.5rem, preferred: 2.5vw, max: 3rem */
}
.container {
padding: clamp(1rem, 5vw, 3rem);
/* Works for any numeric value */
}
How it works:
- Browser calculates
2.5vwbased on viewport - If result <
1.5rem, uses1.5rem - If result >
3rem, uses3rem - Otherwise, uses the calculated value
Replaces this old pattern:
/* β Old way */
h1 { font-size: 1.5rem; }
@media (min-width: 768px) {
h1 { font-size: 2rem; }
}
@media (min-width: 1200px) {
h1 { font-size: 3rem; }
}
Modern Viewport Unitsβ
Why it matters: 100vh breaks on mobile browsers due to address bars. Modern units fix this.
/* β Old way - breaks on mobile */
.hero {
height: 100vh;
}
/* β
Modern way */
.hero {
height: 100dvh; /* Dynamic viewport height */
}
The three modern units:
| Unit | Name | Behavior |
|---|---|---|
svh | Small Viewport Height | Smallest possible viewport (with address bar visible) |
lvh | Large Viewport Height | Largest possible viewport (address bar hidden) |
dvh | Dynamic Viewport Height | Adjusts as address bar shows/hides β |
Which to use:
- Most cases: Use
dvh(smoothly adapts to mobile browsers) - Fixed content: Use
svh(ensures content always visible) - Fullscreen effects: Use
lvh(maximizes space when possible)
Aspect Ratioβ
Why it matters: No more padding-bottom hacks for maintaining aspect ratios.
/* β Old way */
.video-wrapper {
position: relative;
padding-bottom: 56.25%; /* 16:9 ratio hack */
}
/* β
Modern way */
.video {
aspect-ratio: 16 / 9;
}
.square {
aspect-ratio: 1;
}
.portrait {
aspect-ratio: 3 / 4;
}
Real-world use cases:
- Video embeds
- Image containers
- Card thumbnails
- Placeholder elements
Visual Effectsβ
Backdrop Filterβ
Why it matters: Creates glassmorphism effects without complex layering. Used in modern UIs everywhere.
.glass-modal {
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.6);
border: 1px solid rgba(255, 255, 255, 0.8);
}
.glass-navbar {
backdrop-filter: blur(8px) saturate(180%);
background: rgba(255, 255, 255, 0.7);
}
Available filters:
blur()β Background blurbrightness()β Lighten/darkencontrast()β Adjust contrastsaturate()β Color intensityhue-rotate()β Color shift
Real-world use cases:
- Modals and overlays
- Navigation bars
- Floating panels
- macOS/iOS-style interfaces
Performance note: Use sparingly on large areas; can be GPU-intensive.
Filterβ
Why it matters: Apply visual effects without Photoshop or image manipulation.
img {
filter: grayscale(100%);
}
img:hover {
filter: grayscale(0%);
transition: filter 0.3s ease;
}
.sepia {
filter: sepia(80%);
}
.dark-mode img {
filter: brightness(0.8) contrast(1.2);
}
Common filters:
grayscale(0-100%)blur(px)brightness(0-200%)contrast(0-200%)drop-shadow()hue-rotate(deg)invert(0-100%)opacity(0-100%)saturate(0-200%)sepia(0-100%)
Interaction & UXβ
Scroll Behaviorβ
Why it matters: Smooth scrolling without JavaScript.
html {
scroll-behavior: smooth;
}
Works with:
- Anchor links (
<a href="#section">) scrollIntoView()JavaScript calls- Browser back/forward navigation
Note: Respects prefers-reduced-motion automatically in modern browsers.
Scroll Snapβ
Why it matters: Create carousel-like experiences with pure CSS.
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 16px;
}
.slide {
scroll-snap-align: center;
flex-shrink: 0;
width: 80vw;
}
Scroll snap types:
x mandatoryβ Always snaps horizontallyy mandatoryβ Always snaps verticallyx proximityβ Snaps if close enoughboth mandatoryβ Snaps in both directions
Alignment options:
startβ Snap to start of containercenterβ Snap to centerendβ Snap to end
Real-world use cases:
- Image carousels
- Onboarding flows
- Card sliders
- Fullscreen sections
Overscroll Behaviorβ
Why it matters: Prevents scroll chaining on mobile. Essential for modals and nested scroll areas.
body {
overscroll-behavior: contain;
/* Prevents pull-to-refresh and navigation gestures */
}
.modal {
overscroll-behavior: contain;
/* Stops scroll from propagating to page behind */
}
Values:
autoβ Default browser behaviorcontainβ Prevents scroll chainingnoneβ Prevents scroll chaining AND effects (pull-to-refresh)
Performance Optimizationβ
will-changeβ
Why it matters: Hints to browser what will animate, optimizing rendering.
.card {
will-change: transform;
}
.animated-box {
will-change: transform, opacity;
}
β οΈ CRITICAL WARNINGS:
- Don't use everywhere (counterproductive)
- Apply only before animation starts
- Remove after animation completes (if possible)
- Browser already optimizes common properties
Interview trap question: "Should you add will-change to every animated element?"
Correct answer: "No. Overuse consumes memory and degrades performance. Use sparingly and only for complex animations with performance issues."
When to actually use:
- Complex animations
- Frequent transform/opacity changes
- 60fps requirements
- After profiling shows rendering bottleneck
content-visibilityβ
Why it matters: Massive performance boost for long pages. Can improve initial render by 50%+.
.section {
content-visibility: auto;
contain-intrinsic-size: 600px; /* Estimated height for layout */
}
How it works:
- Browser skips rendering offscreen content
- Maintains layout space with
contain-intrinsic-size - Automatically renders when scrolled into view
Real-world impact:
- Long blog posts: ~40% faster render
- Infinite scroll: Significantly reduced memory
- Dashboard cards: Improved time-to-interactive
Best practices:
- Use on repeating sections
- Provide accurate
contain-intrinsic-size - Test with actual content
containβ
Why it matters: Isolates component rendering, preventing layout thrashing.
.component {
contain: layout paint;
}
.strict-isolation {
contain: strict; /* layout + paint + size */
}
Containment types:
layoutβ Isolates layout calculationspaintβ Content won't paint outside boundssizeβ Size independent of childrenstyleβ Scoped counter/quote styles
Real-world use cases:
- Widget components
- Third-party embeds
- Dynamic content sections
- Repeated list items
Modern Selectorsβ
:has()
Why it matters: Parent selectors without JavaScript. Game-changing for conditional styling.
/* Card with image gets different styling */
.card:has(img) {
display: grid;
grid-template-columns: 200px 1fr;
}
/* Form with error */
.form:has(.error) {
border-color: red;
}
/* Article without images */
.article:not(:has(img)) {
max-width: 65ch;
}
Advanced patterns:
/* Parent based on sibling state */
.nav:has(+ .hero) {
position: absolute;
}
/* Adjacent elements */
h2:has(+ p) {
margin-bottom: 0.5rem;
}
/* Multiple conditions */
.card:has(.featured):has(.new) {
border: 2px solid gold;
}
Replaces JavaScript patterns:
- Parent class toggling
- Conditional rendering
- State-based styling
:where() and :is()β
Why it matters: Simplified selector grouping with controllable specificity.
/* :is() - normal specificity */
:is(h1, h2, h3) {
margin-top: 0;
line-height: 1.2;
}
/* :where() - zero specificity */
:where(h1, h2, h3) {
margin: 0;
}
Key difference:
/* :is() takes highest specificity */
:is(.class, #id) { } /* specificity: 1-0-0 (id) */
/* :where() always has zero specificity */
:where(.class, #id) { } /* specificity: 0-0-0 */
When to use :where():
- Utility/reset styles (easy to override)
- Default styles
- Framework base styles
When to use :is():
- Normal component styles
- When specificity matters
- Replacing complex selector lists
Animationsβ
Transform-based Animationsβ
Why it matters: Only transform and opacity trigger GPU acceleration for smooth 60fps animations.
/* β
Performant */
.card {
transition: transform 0.2s ease, opacity 0.2s ease;
}
.card:hover {
transform: translateY(-8px) scale(1.02);
}
/* β Causes layout thrashing */
.card:hover {
top: -8px; /* Triggers layout */
width: 102%; /* Triggers layout */
}
Recommended properties to animate:
transform(translate, scale, rotate)opacity
Properties to avoid animating:
width,heightβ Usescale()insteadtop,leftβ Usetranslate()insteadmargin,paddingβ Usetranslate()or layout shifts
Common animation pattern:
@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.element {
animation: slideIn 0.3s ease-out;
}
prefers-reduced-motionβ
Why it matters: Accessibility requirement. Some users experience motion sickness from animations.
/* Default: smooth animations */
.card {
transition: transform 0.3s ease;
}
/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
What to disable:
- Automatic animations
- Parallax effects
- Infinite animations
- Smooth scrolling
- Complex transitions
What to keep:
- Hover state changes (instant)
- User-triggered interactions
- Loading states (simplified)
Container Queriesβ
Core Conceptβ
The mental model: Media queries are for page layout. Container queries are for component layout.
/* Component defines itself as a container */
.card {
container-type: inline-size;
/* or: container-name: card; */
}
/* Component adapts based on ITS OWN width, not viewport */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Key difference from media queries:
/* β Media query: breaks in sidebar/modal */
@media (min-width: 768px) {
.card { flex-direction: row; }
}
/* β
Container query: works anywhere */
@container (min-width: 400px) {
.card { flex-direction: row; }
}
When to Use Container Queriesβ
1. Reusable Components (MOST IMPORTANT)β
.product-card {
container-type: inline-size;
}
@container (min-width: 500px) {
.product-card {
grid-template-columns: 1fr 2fr;
}
}
Why: Card works in full-width page, sidebar, modal, or grid without changes.
2. Design Systems / Component Librariesβ
Scenario: You're building a component that others will use in unknown layouts.
.widget {
container-type: inline-size;
}
/* Widget adapts to wherever it's placed */
@container (min-width: 300px) {
.widget__content { display: flex; }
}
3. Nested Responsive Layoutsβ
Scenario: Component inside variable-width parent.
<div class="dashboard"> <!-- 1400px wide -->
<aside class="sidebar"> <!-- 300px wide -->
<div class="card"> <!-- Needs to be narrow layout -->
</div>
</aside>
<main> <!-- 1100px wide -->
<div class="card"> <!-- Needs to be wide layout -->
</div>
</main>
</div>
Solution: Container queries let cards adapt independently.
4. Micro-frontends / Embeddable Widgetsβ
Why: You don't know the host page viewport, but you know your container width.
.embedded-widget {
container-type: inline-size;
}
/* Reliable regardless of iframe or host page */
@container (min-width: 600px) {
.embedded-widget { /* wide layout */ }
}
5. Grid-based Dashboardsβ
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.dashboard-card {
container-type: inline-size;
}
/* Cards adapt as grid columns resize */
@container (min-width: 450px) {
.dashboard-card__content {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
When NOT to Use Container Queriesβ
β 1. Page-level Layoutβ
/* DON'T: Use media queries for this */
@media (min-width: 768px) {
.sidebar { display: block; }
.main { grid-column: 2; }
}
β 2. Device-specific Behaviorβ
/* DON'T: Use appropriate media queries */
@media (pointer: coarse) { /* Touch devices */
button { min-height: 44px; }
}
@media (hover: hover) { /* Devices with hover */
.card:hover { transform: scale(1.05); }
}
β 3. Single-use Componentsβ
If component:
- Exists only once in app
- Tightly coupled to specific layout
- Never reused
Then: Media queries are simpler.
Container Query Production Patternβ
/* 1. Define container */
.card {
container-type: inline-size;
container-name: card; /* Optional: for specific targeting */
}
/* 2. Default (small) layout */
.card {
display: flex;
flex-direction: column;
}
/* 3. Adapted layouts */
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
}
@container card (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
Decision Checklistβ
| Question | If YES β Use |
|---|---|
| Is this a reusable component? | Container queries |
| Will it appear in unknown layouts? | Container queries |
| Is this page-level structure? | Media queries |
| Is this device-specific behavior? | Media queries |
| Does component need self-awareness? | Container queries |
Production Starter Templateβ
Complete CSS Reset + Defaultsβ
/* ================================
Modern CSS Reset
================================ */
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html {
color-scheme: light dark;
hanging-punctuation: first last;
}
body {
min-height: 100dvh;
line-height: 1.6;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
input,
button,
textarea,
select {
font: inherit;
}
button {
all: unset;
cursor: pointer;
}
a {
color: inherit;
text-decoration: none;
}
Design Tokens (CSS Variables)β
/* ================================
Design Tokens
================================ */
:root {
/* Colors */
--color-bg: #ffffff;
--color-text: #0f172a;
--color-muted: #64748b;
--color-primary: #2563eb;
--color-danger: #dc2626;
/* Spacing Scale */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-8: 48px;
--space-10: 64px;
/* Border Radius */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.15);
/* Typography */
--font-sans: system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, Ubuntu, Cantarell, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco,
Consolas, monospace;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #020617;
--color-text: #e5e7eb;
--color-muted: #94a3b8;
}
}
Fluid Typographyβ
/* ================================
Typography
================================ */
body {
font-family: var(--font-sans);
font-size: clamp(14px, 1vw + 0.5rem, 16px);
color: var(--color-text);
background-color: var(--color-bg);
}
h1 {
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.1;
font-weight: 700;
}
h2 {
font-size: clamp(1.5rem, 3vw, 2.25rem);
line-height: 1.2;
font-weight: 600;
}
h3 {
font-size: 1.25rem;
line-height: 1.3;
font-weight: 600;
}
p {
color: var(--color-muted);
max-width: 70ch;
line-height: 1.7;
}
Layout Utilitiesβ
/* ================================
Layout Utilities
================================ */
.container {
width: min(1200px, 100% - 2rem);
margin-inline: auto;
}
.flex {
display: flex;
gap: var(--space-4);
}
.grid {
display: grid;
gap: var(--space-4);
}
.center {
display: grid;
place-items: center;
}
/* Stack: vertical spacing between elements */
.stack > * + * {
margin-top: var(--space-4);
}
Component Stylesβ
/* ================================
Components
================================ */
/* Card */
.card {
background: var(--color-bg);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
padding: var(--space-5);
border: 1px solid rgba(0, 0, 0, 0.05);
}
/* Button */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-md);
background: var(--color-primary);
color: white;
font-weight: 500;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn:active {
transform: translateY(0);
}
/* Input */
.input {
padding: var(--space-2) var(--space-3);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: var(--radius-md);
background: var(--color-bg);
color: var(--color-text);
}
.input:focus {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
Accessibilityβ
/* ================================
Accessibility
================================ */
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Screen reader only */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
Performanceβ
/* ================================
Performance Optimization
================================ */
/* Lazy-load sections */
.lazy-section {
content-visibility: auto;
contain-intrinsic-size: 600px;
}
/* Isolate components */
.isolated {
contain: layout paint;
}
Interaction Polishβ
/* ================================
Interaction & Scroll
================================ */
html {
scroll-behavior: smooth;
}
.scroll-x {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: var(--space-4);
padding: var(--space-4);
}
.scroll-x > * {
scroll-snap-align: center;
flex-shrink: 0;
}
body {
overscroll-behavior: contain;
}
File Structureβ
styles/
βββ reset.css # CSS reset + defaults
βββ tokens.css # Design system variables
βββ typography.css # Font styles + scale
βββ layout.css # Layout utilities
βββ components/
β βββ button.css
β βββ card.css
β βββ input.css
β βββ modal.css
βββ utilities.css # Helper classes
βββ main.css # Imports all above
For large applications, use @layer:
@layer reset, tokens, layout, components, utilities;
@import "reset.css" layer(reset);
@import "tokens.css" layer(tokens);
/* ... */
Interview Tipsβ
What Senior Interviewers Want to Hearβ
Layout:
"I use Grid for two-dimensional layouts and Flexbox for one-dimensional. I rely on
gapinstead of margins, and useminmax()withauto-fitfor responsive grids."
Responsive Design:
"I use
clamp()for fluid typography to eliminate media query breakpoints,dvhinstead ofvhfor mobile-safe viewport heights, and container queries for component-level responsiveness."
Performance:
"I use
content-visibility: autofor long pages,containfor component isolation, and avoid animating layout-triggering propertiesβonlytransformandopacity."
Modern Selectors:
"
:has()lets me style parents based on children without JavaScript. I use:where()for zero-specificity utilities and:is()for grouped selectors."
Container Queries:
"Media queries handle page layout; container queries handle component layout. I use container queries for reusable components that appear in multiple contexts."
Common Interview Trapsβ
Q: "Should you use will-change on all animated elements?"
β
Correct: "No. Overuse degrades performance. Only use after profiling identifies rendering bottlenecks."
Q: "Can container queries replace media queries?" β Correct: "No, they complement each other. Media queries for viewport-level concerns, container queries for component-level."
Q: "Why not animate width and height?"
β
Correct: "They trigger layout recalculation on every frame. Use transform: scale() instead for GPU-accelerated animations."
Q: "What's the difference between auto-fit and auto-fill?"
β
Correct: "auto-fit collapses empty tracks and stretches items to fill space. auto-fill preserves empty tracks even with few items."
Q: "When should you use dvh vs vh?"
β
Correct: "Always use dvh on mobile to account for dynamic browser UI. vh breaks when address bars appear/disappear."
Red Flags (Things NOT to Say)β
β "I use !important to fix specificity issues"
β
"I structure CSS with proper specificity hierarchy and use :where() for utilities"
β "I animate left and top for movement"
β
"I use transform: translate() for performant animations"
β "Container queries replace media queries" β "They complement each otherβmedia queries for viewport, container queries for components"
β "I add will-change to everything that moves"
β
"I use will-change sparingly after profiling identifies bottlenecks"
Advanced CSS Features (Bonus)β
@layer (CSS Cascade Layers)β
Why it matters: Explicit control over CSS cascade without specificity wars.
/* Define layer order upfront */
@layer reset, base, components, utilities;
/* Add styles to specific layers */
@layer reset {
* { margin: 0; padding: 0; }
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
}
How cascade works with layers:
- Unlayered styles (highest priority)
- Layers in declared order
- Specificity only matters within same layer
Real-world benefit:
/* Utility always wins, even with low specificity */
@layer components {
.button.primary.large { padding: 20px; }
}
@layer utilities {
.p-0 { padding: 0; } /* β
This wins */
}
Subgridβ
Why it matters: Grid items can inherit parent grid tracks.
.parent {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 20px;
}
.child {
display: grid;
grid-template-columns: subgrid; /* Inherits parent columns */
grid-column: span 3;
}
Without subgrid (problems):
/* β Child grid doesn't align with parent */
.child {
display: grid;
grid-template-columns: 1fr 2fr 1fr; /* Must manually match */
}
Real-world use cases:
- Card grids with aligned content
- Form layouts
- Magazine-style layouts
- Dashboard widgets
Example: Card alignment
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
/* All card titles, images, and buttons align across columns */
.card__image { grid-row: 1; }
.card__title { grid-row: 2; }
.card__button { grid-row: 3; }
accent-colorβ
Why it matters: One-line form styling that respects system colors.
:root {
accent-color: #2563eb;
}
/* Now all native form controls use this color */
input[type="checkbox"] { /* automatically styled */ }
input[type="radio"] { /* automatically styled */ }
input[type="range"] { /* automatically styled */ }
progress { /* automatically styled */ }
Before accent-color:
/* β Required custom HTML + CSS for each control */
input[type="checkbox"] {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
/* ...50 more lines */
}
After accent-color:
/* β
One line */
accent-color: #2563eb;
color-mix()β
Why it matters: Mix colors in CSS without preprocessors.
:root {
--color-primary: #2563eb;
}
.button {
background: var(--color-primary);
}
.button:hover {
/* Mix primary with white for lighter shade */
background: color-mix(in srgb, var(--color-primary) 80%, white);
}
.button:active {
/* Mix with black for darker shade */
background: color-mix(in srgb, var(--color-primary) 80%, black);
}
Advanced patterns:
/* Semi-transparent overlays */
.overlay {
background: color-mix(in srgb, black 50%, transparent);
}
/* Theme variations */
.success {
--base: #10b981;
color: color-mix(in srgb, var(--base) 90%, black);
background: color-mix(in srgb, var(--base) 10%, white);
}
Color spaces:
srgbβ Standard RGBhslβ Hue, Saturation, Lightnessoklchβ Perceptually uniform (recommended for design systems)
:focus-visibleβ
Why it matters: Show focus only for keyboard navigation, not mouse clicks.
/* β Old way: focus ring on mouse click too */
button:focus {
outline: 2px solid blue;
}
/* β
Modern way: focus ring only for keyboard */
button:focus-visible {
outline: 2px solid blue;
}
Real-world pattern:
/* Remove default focus (only for mouse) */
button:focus {
outline: none;
}
/* Add custom focus (only for keyboard) */
button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
text-wrapβ
Why it matters: Better text wrapping control.
/* Prevent orphans (single words on last line) */
h1 {
text-wrap: balance;
}
/* Prevent awkward line breaks */
p {
text-wrap: pretty;
}
Comparison:
/* Default wrapping */
text-wrap: wrap;
/* Balance: more even line lengths */
text-wrap: balance; /* Best for headlines */
/* Pretty: avoids orphans */
text-wrap: pretty; /* Best for paragraphs */
/* Stable: doesn't reflow on edits */
text-wrap: stable; /* Best for editable text */
scrollbar-gutterβ
Why it matters: Prevents layout shift when scrollbar appears.
html {
scrollbar-gutter: stable;
}
Problem it solves:
Page with no scrollbar β 100% width
User adds content β scrollbar appears β width decreases β layout shifts
Solution:
/* Always reserve space for scrollbar */
scrollbar-gutter: stable;
/* Content width stays consistent */
overscroll-behavior-block / inlineβ
Why it matters: Fine-grained control over scroll chaining in specific directions.
/* Prevent vertical scroll chaining */
.modal {
overscroll-behavior-block: contain;
/* User can still horizontal scroll to parent */
}
/* Prevent horizontal scroll chaining */
.carousel {
overscroll-behavior-inline: contain;
}
insetβ
Why it matters: Shorthand for top, right, bottom, left.
/* β Old way */
.overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
/* β
Modern way */
.overlay {
position: absolute;
inset: 0;
}
/* With different values */
.element {
inset: 10px 20px 30px 40px; /* top right bottom left */
inset: 10px 20px; /* vertical horizontal */
}
/* Logical properties */
.element {
inset-block: 0; /* top + bottom */
inset-inline: 20px; /* left + right */
}
object-fit & object-positionβ
Why it matters: Control how images fill containers without distortion.
.avatar {
width: 100px;
height: 100px;
object-fit: cover; /* Crop to fill */
object-position: center top; /* Focus on top */
}
.logo {
width: 200px;
height: 100px;
object-fit: contain; /* Scale to fit, preserve ratio */
}
object-fit values:
fillβ Stretch to fill (default, distorts)containβ Scale to fit, preserve ratiocoverβ Scale to fill, crop excessnoneβ Don't resizescale-downβ Usenoneorcontain, whichever is smaller
scroll-margin & scroll-paddingβ
Why it matters: Offset scroll position for fixed headers or padding.
/* Fixed header is 80px tall */
header {
position: fixed;
height: 80px;
}
/* Anchor links should scroll 80px before target */
section {
scroll-margin-top: 80px;
}
/* OR add padding to scroll container */
main {
scroll-padding-top: 80px;
}
Use cases:
- Fixed navigation headers
- Sticky table headers
- Scroll snap with spacing
- Anchor link offsets
Custom Properties (Advanced Patterns)β
Why it matters: Dynamic, context-aware styling without JavaScript.
Pattern 1: Scoped Variables
.card {
--card-bg: white;
--card-padding: 1rem;
background: var(--card-bg);
padding: var(--card-padding);
}
.card.large {
--card-padding: 2rem; /* Override in context */
}
Pattern 2: Computed Values
:root {
--spacing-unit: 8px;
--spacing-2: calc(var(--spacing-unit) * 2); /* 16px */
--spacing-3: calc(var(--spacing-unit) * 3); /* 24px */
}
Pattern 3: Property Fallbacks
.element {
color: var(--theme-color, #2563eb); /* Fallback if not defined */
}
Pattern 4: Type-safe Custom Properties with @property
@property --rotation {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.spinner {
--rotation: 45deg;
transform: rotate(var(--rotation));
}
Modern Defaults Checklistβ
Every project should start with these:
/* ================================
Modern Defaults Checklist
================================ */
/* β
Box model fix */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* β
Remove default margins */
* {
margin: 0;
}
/* β
Mobile-safe viewport height */
body {
min-height: 100dvh;
}
/* β
Responsive images */
img {
max-width: 100%;
height: auto;
display: block;
}
/* β
Inherit fonts for form controls */
input,
button,
textarea,
select {
font: inherit;
}
/* β
Better line height */
body {
line-height: 1.6;
}
/* β
Smooth scrolling (respects user preference) */
html {
scroll-behavior: smooth;
}
/* β
Focus visible only for keyboard */
:focus:not(:focus-visible) {
outline: none;
}
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
/* β
Prevent scroll chaining */
body {
overscroll-behavior: contain;
}
/* β
Accessibility: reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* β
Text rendering */
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
CSS Architecture Patternsβ
BEM (Block Element Modifier)β
/* Block */
.card { }
/* Element */
.card__title { }
.card__image { }
/* Modifier */
.card--featured { }
.card__title--large { }
Utility-First (Tailwind-style)β
/* Spacing utilities */
.mt-4 { margin-top: 1rem; }
.p-6 { padding: 1.5rem; }
/* Layout utilities */
.flex { display: flex; }
.grid { display: grid; }
/* Responsive utilities */
@media (min-width: 768px) {
.md\:flex-row { flex-direction: row; }
}
CSS Modules Patternβ
/* Component styles are scoped */
.container {
/* Only affects this component */
}
/* Global styles */
:global(.utility) {
/* Available everywhere */
}
Performance Checklistβ
β DOβ
- Animate only
transformandopacity - Use
content-visibility: autofor long lists - Use
containfor independent components - Minimize layout thrashing (batch DOM reads/writes)
- Use
will-changesparingly (remove after animation) - Load critical CSS inline, defer non-critical
- Use CSS Grid/Flexbox instead of floats
- Compress/minify CSS in production
β DON'Tβ
- Animate
width,height,top,left,margin - Overuse
will-change(memory leak) - Use
*selector with complex rules - Use deeply nested selectors (
.a .b .c .d .e) - Import large unused CSS libraries
- Use
@importin production (blocks rendering) - Trigger layout thrashing with alternating read/write
Browser Support Notesβ
Universally Supported (2026)β
β
Flexbox
β
Grid
β
CSS Variables
β
clamp()
β
gap
β
aspect-ratio
β
:is(), :where()
β
dvh, svh, lvh
Very Strong Support (>95%)β
β
:has() (Safari 15.4+, Chrome 105+, Firefox 121+)
β
Container queries (All modern browsers 2023+)
β
accent-color
β
color-mix()
β
text-wrap: balance
Progressive Enhancementβ
β οΈ @property (Chromium only, fallback needed)
β οΈ subgrid (Firefox full support, Safari/Chrome improving)
β οΈ Some color spaces in color-mix()
Fallback strategy:
/* Fallback */
background: #2563eb;
/* Enhanced */
background: color-mix(in oklch, var(--primary) 80%, white);
Quick Reference Tablesβ
Layout Decision Matrixβ
| Scenario | Use |
|---|---|
| One-dimensional (row or column) | Flexbox |
| Two-dimensional (rows AND columns) | Grid |
| Items wrap at viewport breakpoints | Grid + auto-fit |
| Items adapt to own width | Container queries + Flexbox |
| Complex magazine layout | Grid + grid-template-areas |
| Centered content | Grid + place-items: center |
Responsive Property Choiceβ
| Goal | Use |
|---|---|
| Fluid font size | clamp() |
| Mobile-safe full height | 100dvh |
| Maintain aspect ratio | aspect-ratio |
| Component-aware layout | Container queries |
| Page-level layout | Media queries |
| Device capability detection | @media (hover), @media (pointer) |
Animation Performanceβ
| Property | Performance | Alternative |
|---|---|---|
transform | β Excellent (GPU) | - |
opacity | β Excellent (GPU) | - |
width, height | β Poor (layout) | transform: scale() |
top, left | β Poor (layout) | transform: translate() |
margin, padding | β Poor (layout) | transform |
background-color | β οΈ OK (paint) | opacity if possible |
Final Interview Scriptβ
When asked: "Tell me about your CSS approach"
"I structure CSS with modern layout systemsβGrid for two-dimensional layouts and Flexbox for one-dimensional. I use
clamp()for fluid typography, eliminating many breakpoints, anddvhunits for mobile-safe viewport heights.For reusable components, I use container queries so they adapt based on their own width, not the viewport. This is crucial for design systems.
I prioritize performance by using
content-visibility: autofor long pages,containfor component isolation, and animating onlytransformandopacityfor 60fps animations.Modern selectors like
:has()eliminate parent-state JavaScript hacks, and I use:where()for zero-specificity utilities in design systems.I always implement
prefers-reduced-motionfor accessibility, and structure larger projects with@layerto manage cascade complexity."
What to Study Nextβ
For Mid-Level β Senior:
- Container queries (deep understanding)
@layercascade management- Performance profiling (Chrome DevTools)
- CSS-in-JS tradeoffs
- Design system architecture
For Senior β Staff:
- CSS Houdini APIs
- Advanced Grid patterns (subgrid)
- Color science (
oklch, perceptual uniformity) - CSS build optimization
- Framework-specific CSS patterns (React, Vue, Svelte)
Resourcesβ
Official Documentation:
- MDN Web Docs: https://developer.mozilla.org
- CSS Spec: https://www.w3.org/Style/CSS/
Learning:
- CSS Tricks: https://css-tricks.com
- Smashing Magazine: https://www.smashingmagazine.com
Tools:
- Can I Use: https://caniuse.com
- CSS Stats: https://cssstats.com
Communities:
- Frontend Masters
- CSS-Tricks Forums
- Stack Overflow (CSS tag)
Summary: The 80/20 Ruleβ
If you only remember 20% of this guide, remember:
- Grid for 2D, Flexbox for 1D
clamp()for fluid typographydvhinstead ofvh- Container queries for components, media queries for pages
:has()for parent selectors- Animate only
transformandopacity content-visibility: autofor performance- Always implement
prefers-reduced-motion
These eight concepts will get you through 80% of modern CSS challenges and interviews.
End of Guide